﻿using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace FreeDream.Infrastructure
{
    public class RefrashTokenMiddle
    {
        //iss(issuer)：签发人,Token发布者
        //exp(expiration time)：过期时间
        //sub(subject)：主题
        //aud(audience)：受众,Token接受者
        //nbf(Not Before)：生效时间
        //iat(Issued At)：签发时间
        //jti(JWT ID)：编号
        //IssuerSigningKey  签名秘钥

        public RequestDelegate RequestDelegate { get; set; }

        //private IOptionsSnapshot<Config> _config;
        ////RefreshTokenAudience用于设置RefreshToken的接受者，与原Audience值不一致，作用是使RefreshToken不能用于访问应用服务的业务API，而AccessToken不能用于刷新Token。
        public RefrashTokenMiddle(RequestDelegate requestDelegate)
        {
            RequestDelegate = requestDelegate;
        }

        //public async Task InvokeAsync(HttpContext context, IOptionsSnapshot<Config> config)
        public async Task InvokeAsync(HttpContext context)
        {
            //_config = config;
            JwtSecurityToken token = null;
            string authorization = context.Request.Headers["Authorization"];
            if (!string.IsNullOrWhiteSpace(authorization) && authorization.StartsWith("Bearer "))
            {
                token = new JwtSecurityToken(authorization.Substring("Bearer ".Length));
            }
            if (token != null && token.ValidTo.AddMinutes(-150) <= DateTime.UtcNow)
            {
                IEnumerable<Claim> claimsPrincipal = token.Claims;

                context.Response.Headers.Add("X-Refresh-Token",
                await GenerateJSONWebToken(claimsPrincipal));
            }

            await RequestDelegate(context);
        }

        /// <summary>
        /// 生成访问令牌的逻辑
        /// </summary>
        /// <param name="claimsPrincipal"></param>
        /// <returns></returns>
        private async Task<string> GenerateJSONWebToken(IEnumerable<Claim> claimsPrincipal)
        {
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("_config.Value.Key"));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
            var token = new JwtSecurityToken(issuer: "issuer",
              audience: "audience",
              claimsPrincipal,
              expires: DateTime.Now.AddMinutes(120),
              signingCredentials: credentials);
            //var token = new JwtSecurityTokenHandler().WriteToken(token);
            //return token;
            return await Task.FromResult(new JwtSecurityTokenHandler().WriteToken(token));
        }

        /// <summary>
        /// 包含生成刷新令牌的逻辑。为此，我们使用该类生成加密随机数。
        /// </summary>
        /// <returns></returns>
        public string GenerateRefreshToken()
        {
            var randomNumber = new byte[32];
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(randomNumber);
                return Convert.ToBase64String(randomNumber);
            }
        }

        /// <summary>
        /// 从Token中获取用户身份
        /// GetPrincipalFromExpiredToken()用于从过期的访问令牌中获取用户主体。为此，我们使用类的ValidateToken()方法
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        /// <exception cref="SecurityTokenException"></exception>
        private ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
        {
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,//是否验证Issuer
                ValidateAudience = true,//是否验证Audience
                ValidateLifetime = true,//是否验证失效时间
                ClockSkew = TimeSpan.FromSeconds(30),
                ValidateIssuerSigningKey = true,//是否验证SecurityKey
                ValidIssuer = "issuer",
                ValidAudience = "audience",
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("_config.Value.Key"))//拿到SecurityKey
            };
            //JwtSecurityTokenHandler此方法验证令牌并返回ClaimsPrincipal对象
            var tokenHandler = new JwtSecurityTokenHandler();
            SecurityToken securityToken;
            var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken);
            var jwtSecurityToken = securityToken as JwtSecurityToken;
            if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
                throw new SecurityTokenException("Invalid token");
            return principal;
        }

        //public class Config
        //{
        //    public string Issuer { get; set; }

        //    public string Audience { get; set; }

        //    public string IssuerSigningKey { get; set; }

        //    public int AccessTokenExpiresMinutes { get; set; }
        //}
    }


}
